package com.bk.bands;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import com.bk.bands.common.Color;
import com.bk.bands.common.Dimension;
import com.bk.bands.common.Point;
import com.bk.bands.common.Rectangle;
import com.bk.bands.model.Border;
import com.bk.bands.model.Font;
import com.bk.bands.model.Style;
import com.bk.bands.print.Paper;
import com.bk.bands.runtime.BandsException;
import com.bk.bands.template.HorizontalAlign;
import com.bk.bands.template.VerticalAlign;
import com.bk.print.Printer;

public class BitmapPaper implements Paper
{
   private int paperDPI;
   private Dimension paperSizeMM10;
   private Bitmap paperImage;
   private Canvas paperCanvas;
   private Stack<Point> viewPortStack = new Stack<Point>();

   class TypefaceCacheKey
   {
      private String faceName;
      private int typeFaceStyle;

      TypefaceCacheKey(String faceName, int typeFaceStyle)
      {
         this.faceName = faceName;
         this.typeFaceStyle = typeFaceStyle;
      }

      @Override
      public boolean equals(Object o)
      {
         if (this == o) return true;
         TypefaceCacheKey key = (TypefaceCacheKey) o;
         return typeFaceStyle == key.typeFaceStyle && faceName.equals(key.faceName);
      }

      @Override
      public int hashCode()
      {
         return 31 * faceName.hashCode() + typeFaceStyle;
      }
   }

   private Map<TypefaceCacheKey,Typeface> typefaceCache;


   public BitmapPaper(int paperDPI, Dimension paperSizeMM10)
   {
      this.paperDPI = paperDPI;
      this.paperSizeMM10 = paperSizeMM10;
      this.typefaceCache = new LinkedHashMap<TypefaceCacheKey, Typeface>();
   }

   private Bitmap createPaperImage(Dimension pixelSize)
   {
      return Bitmap.createBitmap(pixelSize.width, pixelSize.height, Bitmap.Config.RGB_565);
   }

   public Point popViewPort()
   {
      return viewPortStack.pop();
   }

   public void pushViewPort(Point viewPort)
   {
      viewPortStack.push(new Point(viewPort));
   }

   private int toPaperDots(int mm10)
   {
      return toDots(mm10, paperDPI);
   }

   private int toDots(int mm10, int DPI)
   {
      final double inchTimes100 = 2.54 * 100;
      double inches = mm10 / inchTimes100;
      Double pixels = inches * DPI;
      long rounded = Math.round(pixels);
//      int iii =  pixels.intValue();
      return (int) rounded;//(mm10*DPI)/254;
   }

   public Dimension getPaperSizeInPixels()
   {
      return new Dimension(toPaperDots(paperSizeMM10.width) + 1,toPaperDots(paperSizeMM10.height) + 1);
   }

   private Canvas getPaperCanvas() throws BandsException
   {

      if (paperImage == null)
      {
         if (paperDPI < 100)
         {
            throw new BandsException("Min. DPI is 100");
         }

         if (paperSizeMM10.height <= 0 || paperSizeMM10.width <= 0)
         {
            throw new BandsException("Invalid paper size");
         }

         paperImage = createPaperImage(getPaperSizeInPixels());

      }
      if (paperCanvas == null)
      {
         paperCanvas = new Canvas(paperImage);
         Paint paint = new Paint();
         paint.setStyle(Paint.Style.FILL);
         // make the entire canvas white
         paint.setColor(Color.WHITE.getRGB());
         paperCanvas.drawPaint(paint);
      }

      return paperCanvas;
   }

   private Point calculateViewPort()
   {
      Point viewPort = new Point(0, 0);
      for (Point newOrigin : viewPortStack)
      {
         viewPort.translate(newOrigin.x, newOrigin.y);
      }
      return viewPort;
   }

   public void drawRectangle(Rectangle rect, Border border) throws BandsException
   {
      Canvas canvas = getPaperCanvas();

      Point viewPortOrigin = calculateViewPort();
      Point paperViewPotOrigin = new Point(toPaperDots(viewPortOrigin.x),toPaperDots(viewPortOrigin.y));

      Paint pen = new Paint();
      pen.setStyle(Paint.Style.STROKE);
      pen.setColor(border.getLineColor().getRGB());
      pen.setStrokeWidth(border.getLineWidth());

      canvas.translate(paperViewPotOrigin.x,paperViewPotOrigin.y);
      int l = toPaperDots(rect.x);
      int t = toPaperDots(rect.y);
      int r = toPaperDots(rect.x + rect.width);
      int b = toPaperDots(rect.y + rect.height);

      canvas.drawRect(new Rect(l,t,r,b), pen);
      canvas.translate(-paperViewPotOrigin.x,-paperViewPotOrigin.y);

   }

   private int getTypefaceStyleForFont(Font font)
   {
      if(font.isBold() && font.isItalic())
      {
         return Typeface.BOLD_ITALIC;
      }

      if(font.isBold())
      {
         return Typeface.BOLD;
      }

      if(font.isItalic())
      {
         return Typeface.ITALIC;
      }

      return Typeface.NORMAL;

   }

   private Typeface getTypefaceForFont(Font font)
   {
      int typeFaceStyle = getTypefaceStyleForFont(font);
      final TypefaceCacheKey typeFaceKey = new TypefaceCacheKey(font.getFaceName(), typeFaceStyle);
      Typeface typeFace = typefaceCache.get(typeFaceKey);
      if(typeFace == null)
      {
         typeFace = Typeface.create(font.getFaceName(),typeFaceStyle);
         if(typeFace != null)
         {
            typefaceCache.put(typeFaceKey,typeFace);
         }
         else
         {
            typeFace = Typeface.SANS_SERIF;
         }
      }
      return typeFace;
   }

   private TextPaint getPaintForStyle(Style style)
   {
      TextPaint textPaint = new TextPaint();
      Typeface typeFace = getTypefaceForFont(style.getFont());
      Font styleFont = style.getFont();
      textPaint.setTypeface(typeFace);
      textPaint.setUnderlineText(styleFont.isUnderline());
      textPaint.setStrikeThruText(styleFont.isStrikeout());
      textPaint.setTextSize(Math.abs(styleFont.getHeight()));
      textPaint.setColor(style.getTextColor().getRGB());
      textPaint.setTextAlign(Paint.Align.LEFT);
      return textPaint;
   }

   private Layout.Alignment getLayoutAlignForHorizontalAlign(HorizontalAlign horAlign)
   {
      if(horAlign == null)
      {
         return Layout.Alignment.ALIGN_NORMAL;
      }

      switch (horAlign)
      {
         case CENTER:
            return Layout.Alignment.ALIGN_CENTER;
         case LEFT:
            return Layout.Alignment.ALIGN_NORMAL;
         case RIGHT:
            return Layout.Alignment.ALIGN_OPPOSITE;
      }
      throw new IllegalArgumentException("Unsupported horizontal align: " + horAlign.value());
   }



   public void drawText(String text, Style style, Rectangle rect, Border border) throws BandsException
   {
      if(border != null)
      {
         drawRectangle(rect,border);
      }
      if(text.length() > 0)
      {
         Canvas canvas = getPaperCanvas();
         TextPaint textPaint = getPaintForStyle(style);
         Point viewPortOrigin = calculateViewPort();
         Rectangle textRect = new Rectangle(toPaperDots(viewPortOrigin.x + rect.x),toPaperDots(viewPortOrigin.y + rect.y),
                                             toPaperDots(rect.width),toPaperDots(rect.height));

         StaticLayout layout = new StaticLayout(text,textPaint,textRect.width, getLayoutAlignForHorizontalAlign(style.getHorAlign()),1.0f,1.0f,false);

         int textHeight = layout.getHeight();
         int yOffset = 0;
         VerticalAlign verAlign = style.getVerAlign();
         if(verAlign != null)
         {
            switch (verAlign)
            {
               case BOTTOM:
                  yOffset = textRect.height-textHeight;
                  break;
               case CENTER:
                  yOffset = (textRect.height-textHeight)/2;

            }
         }

         canvas.save();

         canvas.translate(textRect.x,textRect.y);

         canvas.clipRect(0,0,textRect.width,textRect.height);

         canvas.translate(0,yOffset);

         layout.draw(canvas);
         canvas.restore();


      }

   }

   public void drawCircle(Point center, int radius, Border border) throws BandsException
   {
      Canvas canvas = getPaperCanvas();
   }

   public Bitmap getBitmap()
   {
      return paperImage;
   }

}